﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using Tesla.Math;

namespace Tesla.Graphics {
    /// <summary>
    /// Interface defining the methods responsible for drawing graphics and initializing the graphics device. The renderer is the central
    /// component in creating graphics objects, manages render states (including preventing redundant state switching), and includes a render queue
    /// to organize and sort geometry that are to be rendered.
    /// </summary>
    public interface IRenderer : IDisposable {

        /// <summary>
        /// Event for when the renderer is in the process of disposing itself, where it will clean up all resources.
        /// </summary>
        event EventHandler Disposing;

        /// <summary>
        /// Gets if the renderer has been disposed.
        /// </summary>
        bool IsDisposed {
            get;
        }

        /// <summary>
        /// Gets or sets the current camera. A camera owns a viewport and is used to perform culling.
        /// </summary>
        ICamera CurrentCamera {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the render queue that will be used to place renderables, sort them, and render them to the render target.
        /// </summary>
        IRenderQueue RenderQueue {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the viewport which identifies the portion of the render target which we will be rendering onto.
        /// </summary>
        Viewport Viewport {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the rectangle used for scissor testing.
        /// </summary>
        Rectangle ScissorRectangle {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the blend factor which is a constant color used for alpha blending. Default is Color.White. This is a "high frequency" render state,
        /// and setting a BlendState to the renderer will override this value.
        /// </summary>
        Color BlendFactor {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the bitmask which defines which samples can be written during multisampling. Default is -1 (0xffffffff). This is a "high frequency" render
        /// state, and setting a BlendState to the renderer will override this value.
        /// </summary>
        int MultiSampleMask {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the reference value for stencil testing. Default is zero. This is a "high frequency" render state, and setting a BlendState to the
        /// renderer will override this value.
        /// </summary>
        int ReferenceStencil {
            get;
            set;
        }

        /// <summary>
        /// Gets the sampler state collection for setting/getting sampler states used by the pixel shader.
        /// </summary>
        ISamplerStateCollection SamplerStates {
            get;
        }

        /// <summary>
        /// Gets the sampler state collection for setting/getting sampler states used by the vertex shader.
        /// </summary>
        ISamplerStateCollection VertexSamplerStates {
            get;
        }

        /// <summary>
        /// Gets the texture collection for setting/getting textures used by the pixel shader.
        /// </summary>
        ITextureCollection Textures {
            get;
        }

        /// <summary>
        /// Gets the texture collection for setting/getting textures used by the vertex shader.
        /// </summary>
        ITextureCollection VertexTextures {
            get;
        }

        /// <summary>
        /// Gets or sets the BlendState currently active on the device and in the cache. Default is BlendState.Opaque. This is equivalent to using the
        /// Get/ApplyRenderState methods.
        /// </summary>
        BlendState BlendState {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the DepthStencilState currently active on the device and in the cache. Default is DepthStencilState.Default. This is equivalent to
        /// using the Get/ApplyRenderState methods.
        /// </summary>
        DepthStencilState DepthStencilState {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the RasterizerState currently active on the device and in the cache. Default is RasterizerState.CullBackClockwiseFront. This
        /// is the equivalent to using the Get/ApplyRenderState methods.
        /// </summary>
        RasterizerState RasterizerState {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the enforced BlendState, this will override the cached BlendState when ApplyRenderState() is called. 
        /// This is equivalent to using the Get/SetEnforcedState methods.
        /// </summary>
        BlendState EnforcedBlendState {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the enforced DepthStencilState, this will override the cached DepthStencilState when ApplyRenderState() is called. 
        /// This is equivalent to using the Get/SetEnforcedState methods.
        /// </summary>
        DepthStencilState EnforcedDepthStencilState {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the enforced RasterizerState, this will override the cached RasterizerState when ApplyRenderState() is called. 
        /// This is equivalent to using the Get/SetEnforcedState methods.
        /// </summary>
        RasterizerState EnforcedRasterizerState {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the enforced material. This may or may not be ignored when the IRenderable's Render() method is called. If this is not
        /// null, it lets the renderable know what material to use instead of its default material. Set this value to null for default behavior.
        /// </summary>
        Material EnforcedMaterial {
            get;
            set;
        }

        /// <summary>
        /// Gets the graphics adapter that was used to create the renderer.
        /// </summary>
        IGraphicsAdapter Adapter {
            get;
        }

        /// <summary>
        /// Clears all bounded render targets.
        /// </summary>
        /// <param name="color">Color to clear to</param>
        void Clear(Color color);

        /// <summary>
        /// Clears all bounded render targets.
        /// </summary>
        /// <param name="options">Options specifying which buffer to clear - Color, Depth, and Stencil</param>
        /// <param name="color">Color to clear to</param>
        /// <param name="depth">Depth value to clear to</param>
        /// <param name="stencil">Stencil value to clear to</param>
        void Clear(ClearOptions options, Color color, float depth, int stencil);

        /// <summary>
        /// Sets a 2D render target to the first render target slot and unbinds
        /// all currently bound targets.
        /// 
        /// Setting this to null
        /// unbinds all targets and sets the target to the currently active 
        /// swap chain's back buffer.
        /// </summary>
        /// <param name="renderTarget">Render target to bind</param>
        void SetRenderTarget(RenderTarget2D renderTarget);
        
        /// <summary>
        /// Sets a Cube render target, to the first render target slot
        /// and unbinds all currently bound targets. 
        /// 
        /// Setting this to null unbinds all targets and sets the target to the currently active 
        /// swap chain's back buffer.
        /// </summary>
        /// <param name="renderTarget">Render target to bind</param>
        /// <param name="cubeMapFace">Which face of the cube map to bind</param>
        void SetRenderTarget(RenderTargetCube renderTarget, CubeMapFace cubeMapFace);

        /// <summary>
        /// Sets the render targets to the device and unbinds all currently
        /// bound targets. The first target in the array is bound to the first
        /// slot and so forth. The first render target's depth-stencil buffer is used
        /// for all targets.
        /// </summary>
        /// <param name="renderTargets">Array of targets</param>
        void SetRenderTargets(params RenderTargetBinding[] renderTargets);

        /// <summary>
        /// Gets the currently bounded render targets.
        /// </summary>
        /// <returns>Current render targets</returns>
        RenderTargetBinding[] GetRenderTargets();

        /// <summary>
        /// Sets the vertex buffer to the first input slot onto the device.
        /// </summary>
        /// <param name="vertexBuffer">Vertex buffer to bind</param>
        void SetVertexBuffer(VertexBuffer vertexBuffer);

        /// <summary>
        /// Sets the vertex buffer to the first input slot onto the device
        /// </summary>
        /// <param name="vertexBuffer">Vertex buffer to bind</param>
        /// <param name="vertexOffset">Offset from the beginning of the buffer to the first vertex to use (in bytes).</param>
        void SetVertexBuffer(VertexBuffer vertexBuffer, int vertexOffset);

        /// <summary>
        /// Gets the currently bounded vertex buffers.
        /// </summary>
        /// <returns>Current render targets</returns>
        VertexBufferBinding[] GetVertexBuffers();

        /// <summary>
        /// Sets an array of vertex buffers starting from the first input slot.
        /// </summary>
        /// <param name="vertexBuffers">Array of VertexBufferBindings, each binding should specify a vertex buffer, a vertex offset, and an instancing frequency.</param>
        void SetVertexBuffers(params VertexBufferBinding[] vertexBuffers);

        /// <summary>
        /// Sets the active index buffer used for drawing indexed geometry.
        /// </summary>
        /// <param name="indexBuffer">The index buffer to set to the device.</param>
        void SetIndexBuffer(IndexBuffer indexBuffer);

        /// <summary>
        /// Gets the currently bound index buffer.
        /// </summary>
        /// <returns>Current index buffer</returns>
        IndexBuffer GetIndexBuffer();

        /// <summary>
        /// Draws non-indexed geometry.
        /// </summary>
        /// <param name="primType">Type of the primitives to draw</param>
        /// <param name="vertexCount">Number of vertices to draw</param>
        /// <param name="startVertex">Starting index in the vertex buffer at which to read vertices from.</param>
        void Draw(PrimitiveType primType, int vertexCount, int startVertex);

        /// <summary>
        /// Draws indexed geometry.
        /// </summary>
        /// <param name="primType">Type of the primitives to draw.</param>
        /// <param name="indexCount">Number of indices to draw.</param>
        /// <param name="startIndex">Starting index in the index buffer at which to read vertex indices from.</param>
        /// <param name="baseVertex">Offset to add to each vertex index in the index buffer.</param>
        void DrawIndexed(PrimitiveType primType, int indexCount, int startIndex, int baseVertex);

        /// <summary>
        /// Draws indexed, instanced geometry.
        /// </summary>
        /// <param name="primType">Type of the primitives to draw.</param>
        /// <param name="indexCountPerInstance">Number of indices per instance to draw.</param>
        /// <param name="startIndex">Starting index in the index buffer at which to read vertex indices from.</param>
        /// <param name="baseVertex">Offset to add to each vertex index in the index buffer.</param>
        /// <param name="instanceCount">Number of instances to draw.</param>
        void DrawIndexedInstanced(PrimitiveType primType, int indexCountPerInstance, int startIndex, int baseVertex, int instanceCount);

        /// <summary>
        /// Applies the render state to the device. If the state is null, then the default state for its type is used instead. If the renderer is
        /// enforcing a state for the specified state type, that is used instead. If the state to be applied already is present in the cache, the renderer returns,
        /// preventing redundant state switching.
        /// 
        /// It is important to let the renderer take care of state management, and not to set states directly in your shader program, in your effect pass. Otherwise,
        /// the render states may be inconsistent to what is present in the cache.
        /// </summary>
        /// <param name="stateType">Enumerated render state type to apply</param>
        /// <param name="state">Render state to apply to the device, if null the default state is used based on the enumerated type.</param>
        void ApplyRenderState(RenderStateType stateType, RenderState state);

        /// <summary>
        /// Returns the current renderstate of the specified type that has been set to the device, and is present in the cache.
        /// </summary>
        /// <param name="stateType">State type to query</param>
        /// <returns>The render state</returns>
        RenderState GetRenderState(RenderStateType stateType);

        /// <summary>
        /// Clears a specific render state, setting it to its default value on the device.
        /// </summary>
        /// <param name="stateType">State type to reset</param>
        void ClearRenderState(RenderStateType stateType);

        /// <summary>
        /// Clears the renderer's render states, setting all state objects to their default values.
        /// </summary>
        void ClearRenderStates();

        /// <summary>
        /// Sets the enforced state that will override the incoming state in ApplyRenderState() method.
        /// </summary>
        /// <param name="state">State to enforce</param>
        void SetEnforcedState(RenderState state);

        /// <summary>
        /// Returns the enforced state specified by its type, if it exists.
        /// </summary>
        /// <param name="stateType">State's type</param>
        /// <returns>Enforced state, or null if this state type is not enforced</returns>
        RenderState GetEnforcedState(RenderStateType stateType);

        /// <summary>
        /// Clears the specific render state from the renderer.
        /// </summary>
        /// <param name="stateType">State type to clear</param>
        void ClearEnforcedState(RenderStateType stateType);

        /// <summary>
        /// Clears all enforced states from the renderer.
        /// </summary>
        void ClearEnforcedStates();
    }
}
